# 围棋逻辑
import copy
import json

import numpy as np

import src.tool as tool

import src.constant as constant
import os

import matplotlib.pyplot as pyplot

# 是否需要打印日志
log = False


def Print(*args):
    if log:
        print(*args)


class Go:
    # 棋盘
    board = None
    # 棋串
    string = None
    robX = None  # 打劫点X
    robY = None  # 打劫点Y
    isBlack = True  # 目前是谁在走子 默认黑先

    def __init__(self):
        self.board = self.getEmptyBoard()
        self.string = self.getEmptyString()

        self.robX = None  # 打劫点X
        self.robY = None  # 打劫点Y
        self.isBlack = True  # 目前是谁在走子 默认是黑

    # 初始化函数
    def init(self):
        self.board = self.getEmptyBoard()
        self.string = self.getEmptyString()

        self.robX = None  # 打劫点X
        self.robY = None  # 打劫点Y
        self.isBlack = True  # 目前是谁在走子 默认是黑

    def set_board(self, data):
        self.board = data

    # 获取空棋盘
    @staticmethod
    def getEmptyBoard() -> list:
        return [
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        ]

    # 获取空的棋串逻辑
    @staticmethod
    def getEmptyString() -> dict:
        return {
            "black": {},
            "white": {},
            "num": 19
        }

    # 打印棋盘  输出存在一定的问题
    @staticmethod
    def show_board(board: list) -> None:
        Print(pyplot.imshow(board))

    # 获取这个点所在的棋串
    def get_string_info(self, x, y):
        # 越界
        if x < 0 or x > 18 or y < 0 or y > 18:
            return -1

        if self.isBlack:
            color = 1
        else:
            color = -1
        # 目前是黑在下 但这个点返回的是空或者白
        if self.board[x][y] != color:
            return False
        else:
            if self.isBlack:
                subString = self.string["black"]
            else:
                subString = self.string["white"]

            # 存在这个棋串
            # Print("get_string_info,x={},y={}".format(x, y))
            # Print("subString", subString)
            if x in subString and y in subString[x]:
                num = subString[x][y]  # 获取棋串编号
                return num
            else:
                return True

    # 合并棋串的时候 选举出一个基准棋串
    @staticmethod
    def get_src_string(su, sd, sl, sr):
        if su > 0 and not isinstance(su, bool):
            return su
        if sd > 0 and not isinstance(sd, bool):
            return sd
        if sl > 0 and not isinstance(sl, bool):
            return sl
        if sr > 0 and not isinstance(sr, bool):
            return sr

    # 合并目前的棋串
    def combine_current_string(self, x, y, su, sd, sl, sr, src):
        # Print("combine_current_string 传参x={},y={},su={},sd={},sl={},sr={},src={}".format(x, y, su, sd, sl, sr, src))
        # Print("combine_current_string处理前 string->", self.string)
        if self.isBlack:
            subString = self.string["black"]
        else:
            subString = self.string['white']
        if x not in subString:
            subString[x] = {}
        subString[x][y] = src
        subString[src].append({'x': x, 'y': y})
        # 有其他串
        if su > 0 and su != src and not isinstance(su, bool):
            # 合并数组
            # 需要加一个判断 因为Python key不存在就报错了
            if su in subString:
                subString[src].extend(subString[su])
                for key in subString:
                    if key < 19:
                        for subkey in subString[key]:
                            if subString[key][subkey] == su:
                                subString[key][subkey] = src
                # Print("即将删除su->", subString[su])
                del subString[su]

        if sr > 0 and sr != src and not isinstance(sr, bool):
            # 合并数组
            if sr in subString:
                subString[src].extend(subString[sr])
                for key in subString:
                    if key < 19:
                        for subkey in subString[key]:
                            if subString[key][subkey] == sr:
                                subString[key][subkey] = src
                # Print("即将删除sr={}subString = {}->".format(sr, subString))
                del subString[sr]
                # Print("subString->", subString)

        if sd > 0 and sd != src and not isinstance(sd, bool):
            # 合并数组
            if sd in subString:
                subString[src].extend(subString[sd])
                for key in subString:
                    if key < 19:
                        for subkey in subString[key]:
                            if subString[key][subkey] == sd:
                                subString[key][subkey] = src
                # Print("即将删除sd->", subString[sd])
                del subString[sd]

        if sl > 0 and sl != src and not isinstance(sl, bool):
            # 合并数组
            if sl in subString:
                subString[src].extend(subString[sl])
                for key in subString:
                    if key < 19:
                        for subkey in subString[key]:
                            if subString[key][subkey] == sl:
                                subString[key][subkey] = src
                # Print("即将删除sl->", subString[sl])
                del subString[sl]
        return

    # 把目前这一手棋加入到棋串中 不考虑吃子

    def combine(self, x, y, isBlack=None):
        if isBlack is not None:
            self.isBlack = isBlack
        # Print("正在组合x={},y={}".format(x, y))
        up, down, right, left = tool.get4direct(x, y)

        # 获取所在棋串的信息
        # Print("get_string_info su:\n")
        su = self.get_string_info(up['x'], up['y'])
        # Print("get_string_info sd:\n")
        sd = self.get_string_info(down['x'], down['y'])
        # Print("get_string_info sl:\n")
        sl = self.get_string_info(left['x'], left['y'])
        # Print("get_string_info sr:\n")
        sr = self.get_string_info(right['x'], right['y'])
        # Print('su = {},sr={},sd={},sl={}'.format(su, sd, sl, sr))
        # Print("检测前棋串string->", self.string)

        # 判断是否存在棋串
        if (su is False or su == -1) and (sd is False or sd == -1) and (sl is False or sl == -1) and (
                sr is False or sr == -1):
            # Print("combine检测到需要新增 string->", self.string)
            stringNum = self.string["num"]
            if self.isBlack:
                subString = self.string['black']
            else:
                subString = self.string['white']
            # 防止报错的处理
            # Print("subString = ", subString)
            if x not in subString:
                subString[x] = {}
            if stringNum not in subString:
                subString[stringNum] = []

            subString[x][y] = stringNum
            subString[stringNum].append({'x': x, 'y': y})
            self.string["num"] = self.string["num"] + 1
        else:
            # Print("combine检测到需要合并，string->", self.string)
            src = self.get_src_string(su, sd, sl, sr)
            # Print("基准棋串为{}".format(src))
            self.combine_current_string(x, y, su, sd, sl, sr, src)
        # Print("合并完结果为 ", self.string)
        return self.string

    # 落子 修改棋盘即可
    def do_step(self, x, y):
        # 这里再判断一次是为了以后被调用的时候方便
        if x < 0 or x > 18 or y < 0 or y > 18:
            tool.throw("x，y坐标范围必须为0-18\n")
        if self.isBlack:
            num = 1
        else:
            num = -1
        self.board[x][y] = num

    # 检查是否可以落子
    def step_check(self, x, y):
        if self.board[x][y] != 0:
            # Print("无法在已有棋子的位置落子")
            return False

        return True

    def kill(self, x, y, flag):
        # Print("kill传参x = {},y = {},flag = {}".format(x, y, flag))
        # Print('kill->string:', self.string)
        up, down, right, left = tool.get4direct(x, y)
        killed = []
        # 遍历四个方向
        if 0 <= up['x'] < 19 and 0 <= up['y'] < 19:
            if self.board[up['x']][up['y']] == flag:
                res = self.check_kill(up['x'], up['y'], flag)
                if res is not False:
                    killed.append(res)

        if 0 <= down['x'] < 19 and 0 <= down['y'] < 19:
            if self.board[down['x']][down['y']] == flag:
                res = self.check_kill(down['x'], down['y'], flag)
                if res is not False:
                    killed.append(res)

        if 0 <= left['x'] < 19 and 0 <= left['y'] < 19:
            if self.board[left['x']][left['y']] == flag:
                res = self.check_kill(left['x'], left['y'], flag)
                if res is not False:
                    killed.append(res)

        if 0 <= right['x'] < 19 and 0 <= right['y'] < 19:
            if self.board[right['x']][right['y']] == flag:
                res = self.check_kill(right['x'], right['y'], flag)
                if res is not False:
                    killed.append(res)
        return killed

    # 检查这个棋串是否能杀死
    def check_kill(self, x: int, y: int, flag: int):

        if flag == 1:
            color = 'black'
        else:
            color = 'white'
        # Print("check_kill传参,x={},y={},color={}".format(x, y, color))
        subString = self.string[color]
        # Print("subString = {},x= {},y={}".format(subString, x, y))
        num = subString[x][y]
        # Print("check_kill 检查的棋串为", num)
        # Print("self.string->", self.string)
        arr = subString[num]
        # Print("获取棋串", arr)

        for i in range(len(arr)):
            x = arr[i]['x']
            y = arr[i]['y']
            up, down, right, left = tool.get4direct(x, y)

            # 在棋盘内 并且是未落子 肯定是活棋
            if 0 <= up['x'] < 19 and 0 <= up['y'] < 19:
                # Print('up,x,y', up['x'], up['y'], self.board[up['x']][up['y']] == 0)
                if self.board[up['x']][up['y']] == 0:
                    # Print("棋串{}为活棋".format(num))
                    return False

            if 0 <= down['x'] < 19 and 0 <= down['y'] < 19:
                # Print('down,x,y', down['x'], down['y'], self.board[down['x']][down['y']] == 0)
                if self.board[down['x']][down['y']] == 0:
                    # Print("棋串{}为活棋".format(num))
                    return False

            if 0 <= left['x'] < 19 and 0 <= left['y'] < 19:
                # Print('left,x,y', left['x'], left['y'], self.board[left['x']][left['y']] == 0)
                if self.board[left['x']][left['y']] == 0:
                    # Print("棋串{}为活棋".format(num))
                    return False

            if 0 <= right['x'] < 19 and 0 <= right['y'] < 19:
                # Print('right,x,y', right['x'], right['y'], self.board[right['x']][right['y']] == 0)
                if self.board[right['x']][right['y']] == 0:
                    # Print("棋串{}为活棋".format(num))
                    return False
        # Print("-----棋串{}为死棋-----".format(num))
        return num

    # 删除棋串
    def clean_string(self, num: int) -> list:
        # Print("clean_string正在删除棋串:", num)
        if num in self.string['black']:
            flag = 'black'
        elif num in self.string['white']:
            flag = 'white'
        # Python得加这条 Nodejs不需要
        else:
            return []
        killed = []
        arr = self.string[flag][num]
        if arr is None:
            # 防止重复杀棋 其实是落子点的十字是同一个棋串导致的
            # Print("{}已被杀死".format(num))
            return []
        for i in range(len(arr)):
            x = arr[i]['x']
            y = arr[i]['y']
            self.board[x][y] = 0
            if y in self.string[flag][x]:
                self.string[flag][x].pop(y)
                killed.append({'x': x, 'y': y})
        self.string[flag].pop(num)
        return killed

    # 判断是否会导致自己死棋
    def suicide(self, x: int, y: int) -> None:
        if self.board[x][y] == 1:
            color = 'black'
        else:
            color = 'white'
        num = self.string[color][x][y]
        arr = self.string[color][num]
        # 删除自身
        for i in range(len(arr) - 1, -1, -1):
            if arr[i]['x'] == x and arr[i]['y'] == y:
                del arr[i]
                break
        self.board[x][y] = 0
        self.string[color]['x'].pop('y')

    # 解析围棋的sgf文件格式
    @staticmethod
    def parse_sgf(sgf_data: str) -> list:
        # W为白 B为黑，后面的字母是1-19的坐标
        # B[dp];
        arr = sgf_data.split(';')
        res = []
        for i in range(len(arr)):
            step = arr[i]
            if step[0] in constant.COLOR_DICT and step[2] in constant.ALPHABET and step[3] in constant.ALPHABET:
                color = constant.COLOR_DICT[step[0]]
                x = constant.ALPHABET[step[2]]
                y = constant.ALPHABET[step[3]]

                # 棋谱是从1开始数的 这里直接自动减1
                res.append({
                    "color": color,
                    "x": x - 1,
                    "y": y - 1
                })
        return res

    # 解析sgf的额外信息
    @staticmethod
    def parse_additional_sgf(sgf_data: str) -> dict:

        RE = tool.get_sgf_info(sgf_data, "RE", '')  # 游戏结果 如B+R为黑中盘胜 白投降
        PB = tool.get_sgf_info(sgf_data, "PB", '')  # 黑方
        BR = tool.get_sgf_info(sgf_data, "BR", '9d')  # 黑方段位 默认职业九段
        PW = tool.get_sgf_info(sgf_data, "PW", '')  # 白方
        WR = tool.get_sgf_info(sgf_data, "WR", '9d')  # 白方段位 默认职业九段
        KM = tool.get_sgf_info(sgf_data, "KM", '7.5')  # 贴目 默认7.5
        AP = tool.get_sgf_info(sgf_data, "AP", 'Honinbo')  # 对弈平台
        DT = tool.get_sgf_info(sgf_data, "DT", tool.getCurrentTimeStr())  # 对弈时间
        RU = tool.get_sgf_info(sgf_data, "RU =", 'chinese')  # 围棋规则 默认中国规则（黑贴7.5子）
        return {
            "RE": RE, "PB": PB, "BR": BR, "PW": PW, "WR": WR, "KM": KM, "AP": AP, "DT": DT, "RU": RU
        }

    # 把Honinbo能识别的棋谱信息保存成方便入库的信息
    @staticmethod
    def parse_goban_to_sgf(goban) -> object:
        sgf = '('
        for i in range(len(goban)):
            x = constant.ALPHABET[int(goban[i]['x'])]
            y = constant.ALPHABET[int(goban[i]['y'])]
            color = constant.COLOR_DICT[int(goban[i]['color'])]
            temp = ';{}[{}{}]'.format(color, x, y)
            sgf = sgf + temp
        sgf = sgf + ')'
        return {
            "raw": json.dumps(goban),
            'sgf': sgf,
        }

    @staticmethod
    # 找出包含cell的成片的空格
    def find_blank(board, cell) -> dict:
        def _find_blank(board, result, cell):
            i, j = cell
            board[i, j] = 9
            result['cross'].add(cell)

            if i - 1 > -1:
                if board[i - 1, j] == 0:
                    _find_blank(board, result, (i - 1, j))
                elif board[i - 1, j] == 1:
                    result['b_around'].add((i - 1, j))
                elif board[i - 1, j] == 2:
                    result['w_around'].add((i - 1, j))
            if i + 1 < 19:
                if board[i + 1, j] == 0:
                    _find_blank(board, result, (i + 1, j))
                elif board[i + 1, j] == 1:
                    result['b_around'].add((i + 1, j))
                elif board[i + 1, j] == 2:
                    result['w_around'].add((i + 1, j))
            if j - 1 > -1:
                if board[i, j - 1] == 0:
                    _find_blank(board, result, (i, j - 1))
                elif board[i, j - 1] == 1:
                    result['b_around'].add((i, j - 1))
                elif board[i, j - 1] == 2:
                    result['w_around'].add((i, j - 1))
            if j + 1 < 19:
                if board[i, j + 1] == 0:
                    _find_blank(board, result, (i, j + 1))
                elif board[i, j + 1] == 1:
                    result['b_around'].add((i, j + 1))
                elif board[i, j + 1] == 2:
                    result['w_around'].add((i, j + 1))

        result = {'cross': set(), 'b_around': set(), 'w_around': set()}
        _find_blank(board, result, cell)

        return result

    # 找出所有成片的空格
    def find_blanks(self, board) -> list:

        blanks = list()
        while True:
            cells = np.where(board == 0)
            if cells[0].size == 0:
                break

            blanks.append(self.find_blank(board, (cells[0][0], cells[1][0])))

        return blanks

    def check_winner(self, board: list) -> dict:
        # 统计
        board = self.transfer_board(board, -1, 2)
        temp = np.copy(board)
        for item in self.find_blanks(np.copy(board)):
            if len(item['w_around']) == 0:
                v = 3  # 黑空
            elif len(item['b_around']) == 0:
                v = 4  # 白空
            else:
                v = 9  # 单官或公气

            for i, j in item['cross']:
                temp[i, j] = v

        black = temp[temp == 1].size + temp[temp == 3].size
        white = temp[temp == 2].size + temp[temp == 4].size
        common = temp[temp == 9].size

        return {
            "black": black,
            "white": white,
            "common": common
        }

    @staticmethod
    # 将白色从-1转为2
    def transfer_board(board: list, src: int, target: int) -> list:
        for i in range(19):
            for j in range(19):
                if board[i][j] == src:
                    board[i][j] = target
        return board

    # 将sgf转为棋串和棋盘
    def transfer_sgf_to_string_and_board(self, sgf_data: str) -> None:
        self.init()
        data = self.parse_sgf(sgf_data)

        for i in range(len(data)):
            single_dict = data[i]
            x = single_dict['x']
            y = single_dict['y']
            color = single_dict['color']
            x = x - 1
            y = y - 1
            # Print("目前是第{}步棋,x={},y={},color={}".format(i, x, y, color))
            self.simple_go_logic(x, y, color)
        # Print('board->', self.board)
        # Print('string', self.string)

    # Honinbo内部使用的围棋逻辑，即把传进来的坐标转为自己的棋盘信息
    # 不需要判断自身死棋或者打劫 因为传进来的棋谱默认合法
    def simple_go_logic(self, x: int, y: int, color: str) -> None:
        if color == 'black':
            self.isBlack = True
            kill_flag = -1
            self_kill_flag = 1
        else:
            self.isBlack = False
            kill_flag = 1
            self_kill_flag = -1
        # 需要自己组棋盘
        self.board[x][y] = self_kill_flag

        self.combine(x, y)
        kill_res = self.kill(x, y, kill_flag)

        if len(kill_res) > 0:
            for i in range(len(kill_res)):
                self.clean_string(kill_res[i])

    # 返回数据
    def return_data(self, success: bool) -> tuple:
        return success, self.board, self.string, self.robX, self.robY

    # 完整的围棋逻辑
    def go_logic(self, x: int, y: int, color: str) -> tuple:
        # 先拷贝一份 防止出现要类似悔棋的逻辑
        temp_board = copy.deepcopy(self.board)
        temp_string = copy.deepcopy(self.string)
        temp_robx = self.robX
        temp_roby = self.robY
        check_step = self.step_check(x, y)
        if check_step is False:
            return self.return_data(False)
        # 判断自杀逻辑是反过来的
        if color == 'black':
            self.isBlack = True
            kill_flag = -1
            self_kill_flag = 1
        else:
            self.isBlack = False
            kill_flag = 1

            self_kill_flag = -1

        # 需要自己组棋盘
        self.board[x][y] = self_kill_flag
        self.combine(x, y)
        kill_res = self.kill(x, y, kill_flag)
        if len(kill_res) > 0:
            for i in range(len(kill_res)):
                killed = self.clean_string(kill_res[i])
                if len(killed) == 1:
                    # 在这里才判断打劫 只杀了一个子 又正好是打劫点 肯定不对
                    # Print("只吃掉了一个棋子 ", killed)
                    # Print("判断打劫 ,robX ={},robY ={},x={},y={}".format(self.robX, self.robY, x, y))
                    if (self.robX is not None and self.robY is not None) \
                            and int(self.robX) == int(x) and int(self.robY) == int(y):
                        # Print("无法在打劫点落子")
                        self.board = temp_board
                        self.string = temp_string
                        return self.return_data(False)
                    # 如果之前不是打劫点 则更新打劫点
                    self.robX = killed[0]['x']
                    self.robY = killed[0]['y']
                    # Print("保存了目前的打劫点 x = {},y={}".format(self.robX, self.robY))
                else:
                    # 如果之前有打劫点 现在清空
                    self.robX = None
                    self.robY = None
        else:
            # 没杀判断自杀
            self_res = self.check_kill(x, y, self_kill_flag)
            # Print('check_kill res = {}'.format(self_res))

            # 处理失败 一切还原
            if self_res is not False:
                self.board = temp_board
                self.string = temp_string
                self.robX = temp_robx
                self.robY = temp_roby
                return self.return_data(False)
        # Print("go_logic校验成功")
        return self.return_data(True)

    # 解析数据集传来的sgf
    def parse_single_data(self, data: dict) -> tuple:
        sgf: str = data['sgf']

        # 每次清除围棋类的数据 很重要
        self.init()
        parsed_sgf = self.parse_sgf(sgf)
        additional = self.parse_additional_sgf(sgf)
        goban = []
        string_list = []

        for i in range(len(parsed_sgf)):
            x = parsed_sgf[i]['x']
            y = parsed_sgf[i]['y']
            color = parsed_sgf[i]['color']
            success, board, string, robX, robY = self.go_logic(x, y, color)
            goban.append(board)
            string_list.append(string)
        # 游戏结果
        RE = additional['RE']
        # 棋谱记录了胜者就赋值 没有得自己计算
        if RE[0] != 'W' and RE[0] != 'B' and RE != constant.UNKNOWN:
            # 因为出现了平局 这只有ai对局或者特殊情况才会出现 围棋是不可能和棋的 那就自己计算
            RE = constant.UNKNOWN
        if RE != constant.UNKNOWN:
            winner = RE[0:1]
        else:
            # 取出最近的棋盘状态 丢给go分析胜率
            last_goban = copy.deepcopy(goban[len(goban) - 1])
            win_res = self.check_winner(last_goban)
            # 黑棋 比白棋+贴目还多 就是 黑胜
            if win_res['black'] > (win_res['white'] + constant.KOMI):
                winner = 'B'
            else:
                winner = 'W'
        winner = [constant.WINNER[winner]]
        # 组成一个tuple
        return goban, winner, parsed_sgf, string_list

    # 把单个sgf转为神经网络需要的数据
    def parse_sgf_2_network_data(self, sgf: str) -> tuple:
        sgf_dict = {
            'sgf': sgf
        }
        return self.parse_single_data(sgf_dict)

    # 获取这个棋串有多少气 大于threshold就返回-1 不需要讨论
    def get_string_life(self, board, arr, threshold=4) -> int:
        life = 0
        for i in range(len(arr)):

            x = arr[i]['x']
            y = arr[i]['y']
            up, down, left, right = tool.get4direct(x, y)
            life = life + self.get_single_dot_life(up['x'], up['y'], board)
            life = life + self.get_single_dot_life(down['x'], down['y'], board)
            life = life + self.get_single_dot_life(left['x'], left['y'], board)
            life = life + self.get_single_dot_life(right['x'], right['y'], board)
            if life >= threshold:
                return -1
        return life

    # 获取棋盘上某个点的气
    @staticmethod
    def get_single_dot_life(x, y, board) -> int:
        if x < 0 or x > 18 or y < 0 or y > 18:
            return 0

        if board[x][y] == 0:
            return 1
        else:
            return 0

    # 获取当前局面额外的信息
    def get_board_additional(self, board, string, color) -> tuple:
        myself_board = copy.deepcopy(board)
        oppo_board = copy.deepcopy(board)
        # 我方和对方分别只有1、2、3口气的棋盘状态
        my_1 = self.getEmptyBoard()
        my_2 = self.getEmptyBoard()
        my_3 = self.getEmptyBoard()
        oppo_1 = self.getEmptyBoard()
        oppo_2 = self.getEmptyBoard()
        oppo_3 = self.getEmptyBoard()

        # 自己的棋永远是1 对手的永远是-1 这里和围棋的逻辑是不一样的，是为了减少干扰
        my_color = 1
        oppo_color = -1
        if color == 1:
            my_color_text = 'white'
            oppo_color_text = 'black'
        else:
            my_color_text = 'black'
            oppo_color_text = 'white'

        # 将原生的棋盘数据 抽成 将来落子方的棋盘 对手方的棋盘，其他位置全0处理
        for i in range(19):
            for j in range(19):
                # 不是落子方也不是空
                if board[i][j] != color and board[i][j] != 0:
                    myself_board[i][j] = 0
                # 落子方
                if board[i][j] == color:
                    oppo_board[i][j] = 0

        my_string = copy.deepcopy(string[my_color_text])
        oppo_string = copy.deepcopy(string[oppo_color_text])
        my_arr_1 = []
        my_arr_2 = []
        my_arr_3 = []
        oppp_arr_1 = []
        oppp_arr_2 = []
        oppp_arr_3 = []
        for key in my_string:
            # 不是棋串
            if int(key) < 19:
                continue

            arr = my_string[key]
            life = self.get_string_life(board, arr)
            # 这样是为了节约时间
            if life == -1:
                continue

            if life == 1:
                my_arr_1.extend(arr)
            if life == 2:
                my_arr_2.extend(arr)
            if life == 3:
                my_arr_3.extend(arr)

        for key in oppo_string:
            # 不是棋串
            if int(key) < 19:
                continue

            arr = oppo_string[key]
            life = self.get_string_life(board, arr)
            # 这样是为了节约时间
            if life == -1:
                continue

            if life == 1:
                oppp_arr_1.extend(arr)
            if life == 2:
                oppp_arr_2.extend(arr)
            if life == 3:
                oppp_arr_3.extend(arr)
        # 改写数组
        my_1 = self.set_board_by_arr(my_1, my_arr_1, my_color)
        my_2 = self.set_board_by_arr(my_2, my_arr_2, my_color)
        my_3 = self.set_board_by_arr(my_3, my_arr_3, my_color)
        oppo_1 = self.set_board_by_arr(oppo_1, oppp_arr_1, oppo_color)
        oppo_2 = self.set_board_by_arr(oppo_2, oppp_arr_2, oppo_color)
        oppo_3 = self.set_board_by_arr(oppo_3, oppp_arr_3, oppo_color)
        return myself_board, oppo_board, my_1, my_2, my_3, oppo_1, oppo_2, oppo_3

    # 获取最近的7步棋
    def get_last_board(self, board_list: list, index: int, color, oppo_color) -> tuple:
        my_last = []
        oppo_last = []
        start = index - 7
        for i in range(7):
            if start < 0:
                empty = self.getEmptyBoard()
                my_last.append(empty)
                oppo_last.append(empty)
            elif start > len(board_list):
                empty = self.getEmptyBoard()
                my_last.append(empty)
                oppo_last.append(empty)
            else:
                current = board_list[start]
                my_arr = self.set_board_by_color(copy.deepcopy(current), color)
                oppo_arr = self.set_board_by_color(copy.deepcopy(current), oppo_color)
                my_last.append(my_arr)
                oppo_last.append(oppo_arr)
            start = start + 1

        return my_last, oppo_last

    # 返回多个空棋盘
    def get_empty_board_by_num(self, num) -> list:
        res = []
        while num < 0:
            res.append(self.getEmptyBoard())
        return res

    # 根据传入的参数 更新棋盘
    @staticmethod
    def set_board_by_arr(board, arr, color: int):
        for i in range(len(arr)):
            x = arr[i]['x']
            y = arr[i]['y']
            board[x][y] = color
        return board

    # 将棋盘状态转换成棋串string
    def transfer_board_to_string(self, board: list) -> dict:
        self.init()
        string = None
        for i in range(19):
            for j in range(19):
                if board[i][j] != 0:
                    if board[i][j] == 1:
                        self.isBlack = True
                        # Print("检测到黑棋 x = {},y={}".format(i, j))
                        # text = 'black'
                    else:
                        self.isBlack = False
                        # Print("检测到白棋 x = {},y={}".format(i, j))
                        # text = 'white'
                    string = self.combine(i, j)

        # 防止空棋盘导致报错
        if string is None:
            string = self.getEmptyString()
        return string

    # 传入颜色文字转数字
    @staticmethod
    def get_color_num_by_text(color):
        if color == 'black':
            return 1
        else:
            return 0

    # 传入颜色数字转文字
    @staticmethod
    def get_color_text_by_num(color):
        if color == 1 or color == '1':
            return 'black'
        else:
            return 'white'

    # 让棋盘只留下某个颜色
    @staticmethod
    def set_board_by_color(board: list, color: int) -> list:
        for i in range(19):
            for j in range(19):
                if board[i][j] != color:
                    board[i][j] = 0
        return board

    # 当前是黑色 就全1 否则全0
    def get_current_color_board(self, num: int) -> list:
        board = self.getEmptyBoard()
        for i in range(19):
            for j in range(19):
                board[i][j] = num
        return board

    # 获取自己和对手的棋盘 数字全是1
    @staticmethod
    def get_myself_and_oppo_board(board, current_color, oppo_color):
        myself_board = copy.deepcopy(board)
        oppo_board = copy.deepcopy(board)
        for i in range(19):
            for j in range(19):
                if myself_board[i][j] == current_color:
                    myself_board[i][j] = 1
                else:
                    myself_board[i][j] = 0
                if oppo_board[i][j] == oppo_color:
                    oppo_board[i][j] = 1
                else:
                    oppo_board[i][j] = 0
        return myself_board, oppo_board

    # 转换颜色
    @staticmethod
    def reverse_color(color):
        if color == 1:
            return -1
        else:
            return 1

    # 转换文字
    @staticmethod
    def reverse_color_text(color):
        if color == 'black':
            return 'white'
        else:
            return 'black'
